/**
 * Tactics AI controller. 
 * Uses for x-com and ufo controllers
 */
class xT_AIController extends GameAIController;

//=============================================================================
// Variables: General
//=============================================================================
var public ETeams               Team;

var private bool                bDoDebug;

//=============================================================================
// Variables: Firing
//=============================================================================
var public xT_Pawn				EnemyAim; // Location where pawn will firing
var private array<xT_Pawn>      Enemies;

//=============================================================================
// Variables: Follow
//=============================================================================
var public bool                 bFollowLeader;

//=============================================================================
// Variables: Move
//=============================================================================
var public vector               NewDestination;

//=============================================================================
// Variables: Pickup
//=============================================================================
var public xT_DroppedPickup     bItemToPickup;

//=============================================================================
// Variables: Command
//=============================================================================
enum ECommandNames
{
	EC_None,

	EC_MoveToPosition,
	EC_MoveToActor,
	EC_FollowLeader,
	EC_Turn,
	EC_Crouch,
	EC_AttackEnemy,
	EC_CheckNoise,
	EC_MoveFarFromEnemy,
	EC_Pickup,
};

struct SAICommands
{
	var ECommandNames AICommandName;
	var class<xT_AICommand> AICommandClass;
};
var private array<SAICommands>          AICommands;

enum ECommandStates
{
	ECS_None,

	ECS_Idle,
	ECS_DeffendPoint,
	ECS_PatrolPosition,
};

struct SAICommandStates
{
	var ECommandStates AICommandState;
	var class<xT_AICommand> AICommandStateClass;
};
var private array<SAICommandStates>          AICommandStates;

//=============================================================================
// Functions: Main
//=============================================================================
/** This function is seting parameters when controller possess to pawn. Used only to set physics*/
function Possess(Pawn aPawn, bool bVehicleTransition)
{
    if (aPawn.bDeleteMe)
	{
		`Warn(self @ GetHumanReadableName() @ "attempted to possess destroyed Pawn" @ aPawn);
		 ScriptTrace();
		 GoToState('Dead');
    }
	else
	{
		Super.Possess(aPawn, bVehicleTransition);
		Team = xT_Pawn(aPawn).Team;

		SetCommandState(xT_Pawn(aPawn).DefaultAICommandState); 
    }
}

//=============================================================================
// Functions: DEBUG
//=============================================================================
event Tick(float aDeltaTime)
{
	if (bDoDebug) 
		if (xT_Pawn(Pawn).bIsSelected)
		{
			`log(" CommandList : "$CommandList$" Active command : "$GetActiveCommand());
		}
}

//=============================================================================
// Functions: Enemies
//=============================================================================
public function RegisterEnemy(xT_Pawn aNewEnemy)
{
	local xT_Pawn ltmpPawn;
	local bool lbAlreadyRegistered;

	foreach Enemies(ltmpPawn)
	{
		if (ltmpPawn == aNewEnemy) lbAlreadyRegistered = true;
	}

	if (!lbAlreadyRegistered) Enemies.AddItem(aNewEnemy);
}

public function UnRegisterEnemy(xT_Pawn aOldEnemy)
{
	Enemies.RemoveItem(aOldEnemy);
}

public function int GetEnemiesCount()
{
	return Enemies.Length;
}

public function xT_Pawn GetEnemy(int aNumberInArray)
{
	return Enemies[aNumberInArray];
}

public function xT_Pawn GetClosestEnemy()
{
	local xT_Pawn ltmpPawn;
	local int il, jl;

	if (Enemies.Length != 0)
	{
		for (il=0; il < Enemies.Length; ++il)
		{
			if (Enemies[il] == none)
			{
				Enemies.RemoveItem(Enemies[il]);
				break;
			}
			for (jl=0; jl < Enemies.Length; ++jl)
			{
				if (Enemies[jl] == none)
				{
					Enemies.RemoveItem(Enemies[jl]);
					break;
				}
				if (abs(Vsize(Enemies[il].location - pawn.location)) < abs(Vsize(Enemies[jl].location - pawn.location)))
				{
					ltmpPawn = Enemies[il];
					Enemies[il] = Enemies[jl];
					Enemies[jl] = ltmpPawn;
				}
			}
		}
		return Enemies[0]; //return first element as closer enemy
	}
	else return none;
}


//=============================================================================
// Functions: Commands
//=============================================================================
/** Set and execute new command to controller
 * @param aCommandStateName ECommandStates. Name of the command state to be executed.
 */ 
private function SetCommandState(ECommandStates aCommandStateName)
{
	local int il;

	il = AICommandStates.Find('AICommandState', aCommandStateName);

	if (il != -1)
	{
		AICommandStates[il].AICommandStateClass.static.InitCommand(self);
	}
	else `warn("Wrong AI Command State: "$aCommandStateName);
}

//=============================================================================
// Functions: Commands
//=============================================================================
/** Set and execute new command to controller
 * @param aCommandName ECommandNames. Name of the command to be executed.
 * @param bDoNotAbbortPreviousCommand Bool. Set to TRUE if you do not want to abort present command from stack.
 */ 
private function SetCommand(ECommandNames aCommandName, optional bool bDoNotAbbortPreviousCommand)
{
	local int il;

	il = AICommands.Find('AICommandName', aCommandName);

	if (il != -1) 
	{
		if (!bDoNotAbbortPreviousCommand) if (CommandList != GetActiveCommand()) AbortCommand(GetActiveCommand()); //protect initial command from abort, so all others commands will be aborted and pushed to stack
		AICommands[il].AICommandClass.static.InitCommand(self);
	}
	else `warn("Wrong AI Command : "$aCommandName);
}

//=============================================================================
// Functions: Orders
//=============================================================================
public function StartAttackEnemy(xT_Pawn aEnemy, optional bool bDoNotAbbortPreviousCommand)
{
	EnemyAim = aEnemy;
	SetCommand(EC_AttackEnemy, bDoNotAbbortPreviousCommand);
}

public function MoveToPosition(Vector aPosition, optional bool bShouldWalk, optional bool bDoNotAbbortPreviousCommand )
{	
	NewDestination = aPosition;
	SetCommand(EC_MoveToPosition, bDoNotAbbortPreviousCommand);
	//xT_AICommand_Cmd_MoveToPosition(GetActiveCommand()).SetDestination(aPosition, bShouldWalk);
}

public function PickupItem(xT_DroppedPickup aPickup, optional bool bShouldWalk, optional bool bDoNotAbbortPreviousCommand )
{	
	bItemToPickup = aPickup;
	SetCommand(EC_Pickup, bDoNotAbbortPreviousCommand);
	//xT_AICommand_Cmd_MoveToPosition(GetActiveCommand()).SetDestination(aPosition, bShouldWalk);
}

//public function MoveToActor(xT_Pawn aActor, optional bool bDoNotAbbortPreviousCommand )
public function MoveToActor(Actor aActor, optional bool bDoNotAbbortPreviousCommand )
{	
	SetCommand(EC_MoveToActor, bDoNotAbbortPreviousCommand);
	xT_AICommand_Cmd_MoveToActor(GetActiveCommand()).SetDestinationActor(aActor);
}

public function ChangePawnRotation(vector aTurnToNewLocation)
{
	SetCommand(EC_Turn);
	xT_AICommand_Cmd_Turn(GetActiveCommand()).SetTurnPoint(aTurnToNewLocation);
}

public function CrouchOrStandUp()
{
	SetCommand(EC_Crouch);
}

public function FollowToLeader(xT_Pawn_Human aLeader, int aUnitsInMove)
{
	bFollowLeader = !bFollowLeader;
	if (bFollowLeader)
	{
		SetCommand(EC_FollowLeader);
		xT_AICommand_Cmd_MoveForLeader(GetActiveCommand()).SetLeader(aLeader, aUnitsInMove);
	}
}

public function MoveFarFromEnemy()
{
	SetCommand(EC_MoveFarFromEnemy, true);
}
//=============================================================================
// Functions: Other Commands
//=============================================================================
public function CheckNoise(xT_Pawn aPossibleEnemy)
{
	SetCommand(EC_CheckNoise);
	xT_AICommand_Cmd_CheckNoise(GetActiveCommand()).SetPossibleEnemy(aPossibleEnemy);
}

//=============================================================================
// Sensors events
//=============================================================================
event HearNoise( float Loudness, Actor NoiseMaker, optional Name NoiseType )
{
	if (GetActiveCommand() != none)
	xT_AICommand(GetActiveCommand()).Reaction_HearNoise(Loudness, NoiseMaker, NoiseType);
}

event SeePlayer( Pawn Seen )
{
	if (self.bIsPlayer) return; // should not be executed by player pawn
	if ( (xT_Pawn_Human(Seen) != none) && (xT_Pawn_Human(Seen).bInvisibilityActive) ) return;
	xT_AICommand(GetActiveCommand()).Reaction_SeeEnemy(xT_Pawn(Seen));
}

event SeeMonster( Pawn Seen )
{
	xT_AICommand(GetActiveCommand()).Reaction_SeeEnemy(xT_Pawn(Seen));
}

event EnemyNotVisible()
{
	xT_AICommand(GetActiveCommand()).Reaction_EnemyLost();
}

function NotifyTakeHit(Controller InstigatedBy, vector HitLocation, int Damage, class<DamageType> damageType, vector Momentum)
{
	xT_AICommand(GetActiveCommand()).Reaction_UnderEnemyFire(xT_Pawn(InstigatedBy.Pawn));
}

//=============================================================================
// DefaultProperties
//=============================================================================
DefaultProperties
{
	bDoDebug = false //Do AI debug console output or not

	AICommandStates[0] = (AICommandState = ECS_None, AICommandStateClass = none)
	AICommandStates[1] = (AICommandState = ECS_Idle, AICommandStateClass = class'xT_AICommand_Act_Idle')
	AICommandStates[2] = (AICommandState = ECS_DeffendPoint, AICommandStateClass = class'xT_AICommand_Act_DefendPoint')
	AICommandStates[3] = (AICommandState = ECS_PatrolPosition, AICommandStateClass = class'xT_AICommand_Act_PatrolPosition')

	AICommands[0] = (AICommandName = EC_None, AICommandClass = none)
	AICommands[1] = (AICommandName = EC_MoveToPosition, AICommandClass = class'xT_AICommand_Cmd_MoveToPosition')
	AICommands[2] = (AICommandName = EC_MoveToActor, AICommandClass = class'xT_AICommand_Cmd_MoveToActor')
	AICommands[3] = (AICommandName = EC_FollowLeader, AICommandClass = class'xT_AICommand_Cmd_MoveForLeader')
	AICommands[4] = (AICommandName = EC_Turn, AICommandClass = class'xT_AICommand_Cmd_Turn')
	AICommands[5] = (AICommandName = EC_Crouch, AICommandClass = class'xT_AICommand_Cmd_Crouch')
	AICommands[6] = (AICommandName = EC_AttackEnemy, AICommandClass = class'xT_AICommand_Cmd_AttackEnemy')
	AICommands[7] = (AICommandName = EC_CheckNoise, AICommandClass = class'xT_AICommand_Cmd_CheckNoise')
	AICommands[8] = (AICommandName = EC_MoveFarFromEnemy, AICommandClass = class'xT_AICommand_Cmd_MoveFarFromEnemy')
	AICommands[9] = (AICommandName = EC_Pickup, AICommandClass = class'xT_AICommand_Cmd_Pickup')


	Name="Default__xT_AIController"
}
